home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Info-Mac 4
/
Info_Mac IV CD-ROM (Pacific HiTech Inc.)(August 1994).iso
/
Development
/
Source
/
DBL Pascal Library
/
INIT Shell Folder
/
INIT Shell ƒ
/
Shell ƒ
/
INITShellInlines.p
< prev
next >
Wrap
Text File
|
1990-05-28
|
10KB
|
258 lines
unit INITShellInlines;
{Copyright © 1990, David B. Lamkins}
{All rights reserved.}
interface
{•• This unit contains inline routines which depend upon very particular conventions}
{•• for their proper use. Many of these routines modify the runtime stack in a way}
{•• which will cause system crashes if not used according to their directions. Please}
{•• read the comments and accompanying documentation carefully before use…}
{Patches:}
{}
{Register parameters may be accessed and register values returned using the inline functions}
{provided in this unit. The ability to return register values depends upon THINK Pascal}
{generating a simple UNLK, RTS epilog for procedures.}
{}
{Stack parameters are accessed parasitically by offset, and must never be declared. The}
{ability to access stack parameters depends upon THINK Pascal always generating a LINK}
{prologue for procedures.}
{}
{Rather than returning to its caller, the patch code may transfer control to a patched routine}
{by using the SetTrapExit inline procedure declared in this unit. This is the normal and preferred}
{method of handling a trap patch. The alternative requires you to call the patched routine from}
{within this patch code using the inline CallPatchedTrap procedure, and results in a "tail patch".}
{}
{VBLs:}
{}
{VBLs do not have parameters. On entry, A0 points to the VBL's own VBLTask record. You can}
{use GetVBLTask to copy this pointer to a local variable. Your VBL must set its count to a nonzero}
{value if it is to be requeued. See II-349 and TN 180 for further details.}
uses
INITShellGlobals, Retrace;
{This procedure must be called at most once, and must be called from the main routine, not}
{from a nested routine. It modifies the return address on the stack to transfer control via}
{a jump to the stashed address of the patch code, rather than returning to the caller. Control}
{will eventually pass back to the caller when the patched trap returns. Call this procedure}
{just before exit, as late as possible, and *never* more than once! The globals access}
{routines (GlobalsHandle, LockGlobals, UnlockGlobals) depend on this not having been called.}
procedure SetTrapExit;
inline
$54AE, $0004; {ADDQ.L #2,4(A6)}
{This procedure must be called from the main routine, not from a nested routine. It calls}
{the patched trap via its stashed address. Note that it preserves all registers.}
procedure CallPatchedTrap;
inline
$42A7, {CLR.L -(SP) -- make room for return address}
$42A7, {CLR.L -(SP) -- make room for call address}
$2F08, {MOVE.L A0,-(SP) -- save A0}
$41FA, $0012, {LEA $14(PC),A0 -- calculate call return address}
$2F48, $0008, {MOVE.L A0,8(SP) -- stuff return address into stack}
$206E, $0004, {MOVEA.L 4(A6),A0 -- get header return}
$2068, $0004, {MOVEA.L 4(A0),A0 -- fetch address of trapped patch}
$2F48, $0004, {MOVE.L A0,4(SP) -- tuck into stack}
$205F, {MOVEA.L (SP)+,A0 -- restore A0}
$4E75; {RTS -- make the call}
{This procedure must be called from the main routine, not from a nested routine. It deallocates}
{the local stack frame, discards the specified number of bytes of parameters, and returns to}
{the patched trap's caller. This is needed if you want to completely replace the functionality of}
{the original trap with your own code. The only use I can think of for this is in virus protection}
{software that wants to selectively suppress the action of certain traps. Be certain that you}
{pass an even value as the parameter - an odd value will misalign the stack and cause a double}
{fault at runtime.}
procedure DeallocateAndReturn (paramSize: INTEGER);
inline
$206E, $0008, {MOVEA.L 8(A6),A0 -- save original return address}
$301F, {MOVE.W (SP)+,D0 -- get param byte size}
$4E5E, {UNLK A6 -- discard frame}
$4FF7, $0008, {LEA 8(SP,D0.W),SP -- discard header & caller RA, and params}
$4ED0; {JMP (A0) -- return}
{The following functions are used to access parameters passed in registers. In order to capture}
{registers passed to a trap, the GetReg__ functions must be called first thing in the patch code,}
{and may only appear in a statement which assigns the value to a local variable of main.}
{This works because the THINK Pascal compiler is good enough not to shuffle values through}
{registers to do an assignment. The actual code generated looks something like this:}
{ A0 := RegA0; -- the Pascal source}
{ ADDQ.L #4,A7 -- the compiler reserves space for the result}
{ MOVE.L A0,(A7) -- the inline code expansion of function RegA0}
{ MOVE.L (A7)+,-__(A6) -- the assignment to a local variable}
function GetRegA0: LONGINT;
inline
$2E88; {MOVE.L A0,(A7)}
type
VBLTaskPtr = ^VBLTask;
function GetVBLTask: VBLTaskPtr; {an alias for RegA0, to be used in VBLs}
inline
$2E88; {MOVE.L A0,(A7)}
function GetRegA1: LONGINT;
inline
$2E89; {MOVE.L A1,(A7)}
function GetRegD0: LONGINT;
inline
$2E80; {MOVE.L D0,(A7)}
function GetRegD1: LONGINT;
inline
$2E81; {MOVE.L D1,(A7)}
function GetRegD2: LONGINT;
inline
$2E82; {MOVE.L D2,(A7)}
{The following procedures are used to pass register parameters to a patched trap. These}
{calls should immediately preceed ExitToTrap or a call to CallPatchedTrap. As noted above,}
{the THINK Pascal compiler is good enough not to shuffle values through registers, so the}
{order of these calls doesn't matter.}
procedure SetRegA0 (value: LONGINT);
inline
$205F; {MOVEA.L (A7)+,A0}
procedure SetRegA1 (value: LONGINT);
inline
$225F; {MOVEA.L (A7)+,A1)}
procedure SetRegD0 (value: LONGINT);
inline
$201F; {MOVE.L (A7)+,D0}
procedure SetRegD1 (value: LONGINT);
inline
$221F; {MOVE.L (A7)+,D1}
procedure SetRegD2 (value: LONGINT);
inline
$241F; {MOVE.L (A7)+,D2}
{The following functions access parameters parasitically. You need to know the size of}
{each parameter on the stack. The offset you will pass to the following routines is the}
{sum of the stack size of all the parameters which follow it in the parameter list. For}
{example, if you are patching the stack trap:}
{ AddResource(h: Handle; rType: ResType; id: INTEGER; name: STR255);}
{you'll use the following expressions to access the parameters of the caller:}
{ h := Handle(LParam(10));}
{ rType := ResType(LParam(6));}
{ id := WParam(4);}
{ namePtr := StringPtr(LParam(0));}
{}
{These procedures depend upon the stack protocol which is determined by the header code.}
{When these procedures are called, the stack will contain (starting "deep" in the stack) the}
{trap's parameters in left-to-right order, the caller's return address, the header's return}
{address, the frame pointer, and any local variables of the patch code.}
{}
{There is one function for each size of parameter which may be pushed: LParam for 4-byte}
{parameters, WParam for 2-bytes, and BParam for 1-byte parameters. The results will}
{usually have to be typecast.}
{}
{Don't try to be clever and put the parameters in the declaration of main. Remember that the}
{header code will mess with the stack before your main gets control, and since the standard}
{epilogue code may be subverted by the SetTrapExit procedure, your declaration may break}
{even if you account for the return address as an extra parameter.}
function LParam (offset: INTEGER): LONGINT;
inline
$301F, {MOVE.W (A7)+,D0}
$2EB6, $000C; {MOVE.L 12(A6,D0.W),(A7)}
function WParam (offset: INTEGER): INTEGER;
inline
$301F, {MOVE.W (A7)+,D0}
$3EB6, $000C; {MOVE.W 12(A6,D0.W),(A7)}
function BParam (offset: INTEGER): Byte;
inline
$301F, {MOVE.W (A7)+,D0}
$1EB6, $000C; {MOVE.B 12(A6,D0.W),(A7)}
{The following procedures stuff a result into the stack. The comments for the parameter}
{access routines, above, apply here as well.}
procedure LResult (value: LONGINT; offset: INTEGER);
inline
$301F, {MOVE.W (A7)+,D0}
$2D9F, $000C; {MOVE.L (A7)+,12(A6,D0.W)}
procedure WResult (value, offset: INTEGER);
inline
$301F, {MOVE.W (A7)+,D0}
$3D9F, $000C; {MOVE.W (A7)+,12(A6,D0.W)}
procedure BResult (value: Byte; offset: INTEGER);
inline
$301F, {MOVE.W (A7)+,D0}
$1D9F, $000C; {MOVE.B (A7)+,12(A6,D0.W)}
{The following procedures are used to push stack arguments prior to tail-calling a}
{patched stack-based trap. The compiler actually generates the push, the NOP is}
{a placeholder because the compiler expects something after an inline declaration.}
procedure PushL (value: LONGINT);
inline
$4e71; {NOP}
procedure PushW (value: INTEGER);
inline
$4e71; {NOP}
procedure PushB (value: Byte);
inline
$4e71; {NOP}
{The following functions are used to pop a result from the stack after tail-calling a}
{patched stack-based trap. As above, the NOP is only a placeholder.}
function PopL: LONGINT;
inline
$4E71; {NOP}
function PopW: INTEGER;
inline
$4E71; {NOP}
function PopB: Byte;
inline
$4E71; {NOP}
{The following function fetches the handle to the INIT globals. This must be called from}
{the main program, since it relies on finding a return address into the code header at 4(A6).}
function GlobalsHandle: INITGlobalsHandle;
inline
$206E, $0004, {MOVEA.L 4(A6),A0 -- get the address of the return to the header}
$2EA8, $000A; {MOVE.L 10(A0),(A7) -- fetch the globals handle}
{The following procedures are used to lock and unlock the globals handle. They keep the}
{globals handle locked until UnlockGlobals calls balance LockGlobals calls. These must be}
{called from the main program, since they rely on finding a return address into the code}
{ header at 4(A6).}
procedure LockGlobals;
inline
$206E, $0004, {MOVEA.L 4(A6),A0 -- get the address of the return to the header}
$5268, $0008, {ADDQ.W #1,8(A0) -- bump the lock counter up by one}
$2068, $000A, {MOVEA.L 10(A0),A0 -- fetch the globals handle}
$A029; {_HLock -- lock it}
procedure UnlockGlobals;
inline
$206E, $0004, {MOVEA.L 4(A6),A0 -- get the address of the return to the header}
$5368, $0008, {SUBQ.W #1,8(A0) -- bump the lock counter down by one}
$6606, {BNE.S *+8 -- unless unlocks balance locks, skip out}
$2068, $000A, {MOVEA.L 10(A0),A0 -- fetch the globals handle}
$A02A; {_HUnlock -- really unlock it}
{Finally, because I don't always get it right the first time…}
procedure Break;
inline
{$IFC Debugging}
$A9FF; {_Debugger}
{$ELSEC}
$4E71; {NOP}
{$ENDC}
implementation
{It's all in the interface…}
end.